# This file is part of trytond-pcw-stock module for Tryton. 
# The COPYRIGHT file at the top level of this repository contains 
# the full copyright notices and license terms.

from decimal import Decimal
from trytond.model import Workflow, ModelView, ModelSQL, fields
from trytond.pyson import In, Eval, Not, Equal, If, Get, Bool
from trytond.transaction import Transaction
from trytond.pool import Pool, PoolMeta

__all__ = ['Move']
__metaclass__ = PoolMeta

STATES = {
    'readonly': In(Eval('state'), ['cancel', 'assigned', 'done']),
}
DEPENDS = ['state']
TYPES = [
    ('in', 'In'),
    ('out', 'Out'),
    ]

class Move(Workflow, ModelSQL, ModelView):
    "Pcw Stock Move"
    __name__ = 'pcw.stock.move'

    date = fields.Date("Date", select=True, required=True,
        states={
            'readonly': Eval('state') != 'draft',
            })
    type = fields.Selection(TYPES, 'Type', required=True, select=True, states=STATES,
        depends=DEPENDS)
    reference = fields.Char('Reference',
        states={
            'readonly': Eval('state') != 'draft',
            },
        depends=['state'])
    product = fields.Many2One("product.product", "Product", required=True,
        select=True, states=STATES,
        on_change=['product', 'uom'],
        domain=[('type', '!=', 'service')],
        depends=DEPENDS)
    product_uom_category = fields.Function(
        fields.Many2One('product.uom.category', 'Product Uom Category',
            on_change_with=['product']),
        'on_change_with_product_uom_category')
    uom = fields.Many2One("product.uom", "Uom", required=True, states=STATES,
        domain=[
            ('category', '=', Eval('product_uom_category')),
            ],
        on_change=['product', 'currency', 'uom', 'unit_price', 'company'],
        depends=['state', 'product_uom_category'])
    quantity = fields.Float("Quantity", required=True,
        digits=(16, 4), 
        on_change=['quantity','product','uom'], states=STATES, depends=['state'])
    internal_quantity = fields.Float('Internal Quantity', digits=(16, 4),
        required=True)
    state = fields.Selection([
        ('draft', 'Draft'),
        ('assigned', 'Assigned'),
        ('done', 'Done'),
        ('cancel', 'Canceled'),
        ], 'State', select=True, readonly=True, required=True)
    company = fields.Many2One('company.company', 'Company', required=True,
        states={
            'readonly': Not(Equal(Eval('state'), 'draft')),
            },
        domain=[
            ('id', If(In('company', Eval('context', {})), '=', '!='),
                Get(Eval('context', {}), 'company', 0)),
            ],
        depends=['state'], select=True, readonly=True)

    unit_price = fields.Numeric('Unit Price', digits=(16, 4),
        states={
            'required': True,
            'readonly': Not(Equal(Eval('state'), 'draft')),
            },
        depends=['state'])
    currency = fields.Many2One('currency.currency', 'Currency',
        states={
            'required': True,
            'readonly': True,
            },
        depends=['state'])


    @classmethod
    def __setup__(cls):
        super(Move, cls).__setup__()
        cls._sql_constraints += [
            ('check_move_qty_pos',
                'CHECK(quantity >= 0.0)', 'Move quantity must be positive'),
            ('check_move_internal_qty_pos',
                'CHECK(internal_quantity >= 0.0)',
                'Internal move quantity must be positive'),
            ]

        cls._order.insert(0, ('date', 'DESC'))
        cls._order.insert(1, ('id', 'DESC'))

        cls._error_messages.update({
            'del_draft_cancel': ('You can not delete stock move "%s" because '
                'it is not in draft or cancelled state.'),
            'modify_assigned_done_cancel': ('You can not modify stock move "%s"'
                'because it is in "Assigned", "Done" or "Cancel" state.'),
            'snapshot_exists': ('Snapshot with date >= "%s" exists.'),
            'inventory_exists': ('Inventory with date > "%s" exists.'),
            })
        cls._transitions |= set((
                ('done', 'cancel'),
                ('cancel', 'draft'),
                ('draft', 'assigned'),
                ('assigned', 'draft'),
                ('assigned', 'cancel'),
                ('assigned', 'done'),
                ))

        cls._buttons.update({
                'cancel': {
                    'invisible': Eval('state').in_(['cancel', 'draft']),
                    },
                'draft': {
                    'invisible': ~Eval('state').in_(['cancel', 'assigned']),
                    'icon': If(Eval('state') == 'cancel', 'tryton-clear','tryton-go-previous'),
                    },
                'assign': {
                    'invisible': Eval('state') != 'draft',
                    },
                'confirm': {
                    'invisible': Eval('state') != 'assigned',
                    },
                })

    @staticmethod
    def default_state():
        return 'draft'

    @staticmethod
    def default_company():
        return Transaction().context.get('company')

    @staticmethod
    def default_internal_quantity():
        return 0

    @staticmethod
    def default_currency():
        Company = Pool().get('company.company')
        company = Transaction().context.get('company')
        if company:
            company = Company(company)
            return company.currency.id

    @classmethod
    def delete(cls, orders):
        for order in orders:
            if order.state in ['done','assigned']:
                cls.raise_user_error('del_draft_cancel', error_args=(str(order.reference),))                
        super(Move, cls).delete(orders)

    def on_change_product(self):
        pool = Pool()
        Uom = pool.get('product.uom')
        res = {}
        if self.product:
            res['uom'] = self.product.default_uom.id
            res['internal_quantity'] = Decimal('0.0')
            res['unit_price'] = self.product.average_cost     
        return res

    def on_change_with_product_uom_category(self, name=None):
        if self.product:
            return self.product.default_uom_category.id

    def on_change_quantity(self):
        pool = Pool()
        Uom = pool.get('product.uom')
        res = {}  
        if not self.product or not self.uom or not self.quantity:
            return res
        return res        

    def on_change_uom(self):
        pool = Pool()
        Uom = pool.get('product.uom')
        Currency = pool.get('currency.currency')

        res = {
            'unit_price': Decimal('0.0'),
            }

        if self.product and self.uom:
            unit_price = self.product.get_average_cost(self.uom)
            res['unit_price'] = unit_price            

        return res

    def compute_internal_quantity(self):
        if self.product.default_uom.id == self.uom.id:
            return self.quantity 
        Uom = Pool().get('product.uom')
        return Uom.compute_qty(self.uom, self.quantity, self.product.default_uom)

    @classmethod
    @ModelView.button
    @Workflow.transition('cancel')
    def cancel(cls, orders):
        Snapshot = Pool().get('pcw.stock.snapshot')
        Inventory = Pool().get('pcw.stock.inventory')
        InventoryLine = Pool().get('pcw.stock.inventory.line')
        for order in orders:
            if Snapshot.snapshot_exists(order.date, order.company.id):
                cls.raise_user_error('snapshot_exists', error_args=(str(order.date),))

            invs = InventoryLine.search([('move','=',order.id)]) 
            if invs:   
                if Inventory.inventory_exists(order.date, order.company.id, ignore_id=invs[0].inventory.id, sign=">="):
                    cls.raise_user_error('inventory_exists', error_args=(str(order.date),))

    @classmethod
    @ModelView.button
    @Workflow.transition('draft')
    def draft(cls, orders):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('assigned')
    def assign(cls, orders):
        pass

    @classmethod
    @ModelView.button
    @Workflow.transition('done')
    def confirm(cls, orders):
        Snapshot = Pool().get('pcw.stock.snapshot')
        Inventory = Pool().get('pcw.stock.inventory')
        for order in orders:
            if Snapshot.snapshot_exists(order.date, order.company.id):
                cls.raise_user_error('snapshot_exists', error_args=(str(order.date),))
            if Inventory.inventory_exists(order.date, order.company.id):
                cls.raise_user_error('inventory_exists', error_args=(str(order.date),))
            if order.type == 'in':
                order.product.set_average_cost(order.quantity, order.unit_price, order.uom)
        cls.process(orders)

    @classmethod
    def process(cls, orders):
        done = []
        for order in orders:
            if order.state in ('done', 'cancel'):
                continue
            order.internal_quantity=order.compute_internal_quantity()
            order.state='done'
            order.save()



